package com.sleepapp.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sleepapp.dao.UserDao;
import com.sleepapp.entity.CheckInRecord;
import com.sleepapp.dao.CheckInRecordDao;
import com.sleepapp.entity.User;
import com.sleepapp.service.CheckInRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sleepapp.utils.StpUserUtil;
import com.sleepapp.vo.*;
import com.sleepapp.vo.DashboardData.MonthlyStats;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;

/**
 * <p>
 * 打卡记录表 服务实现类
 * </p>
 *
 * @author 
 * @since 2025-03-13
 */
@Service
public class CheckInRecordServiceImpl extends ServiceImpl<CheckInRecordDao, CheckInRecord> implements CheckInRecordService {

    @Autowired
    private UserDao userDao;

    @Override
    public DashboardData dashboard() {
        // 计算本月一共有多少天 去除周五和周六
        // 获取当前时间
        DateTime now = DateUtil.date();

        // 获取本月的第一天和最后一天
        DateTime firstDay = DateUtil.beginOfMonth(now);
        DateTime lastDay = DateUtil.endOfMonth(now);

        long count = 0;
        DateTime current = firstDay;

        // 遍历本月所有日期
        while (DateUtil.compare(current, lastDay) <= 0) {
            // 获取星期几（1=周日，6=周五，7=周六）
            int dayOfWeek = DateUtil.dayOfWeek(current);

            // 排除周五和周六
            if (dayOfWeek != 6 && dayOfWeek != 7) {
                count++;
            }

            // 移动到下一天
            current = DateUtil.offsetDay(current, 1);
        }


        // 查询本月成功的打卡记录
        LambdaQueryWrapper<CheckInRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CheckInRecord::getUserId, StpUserUtil.getLoginIdAsLong())
                .eq(CheckInRecord::getStatus, "success")
                .between(CheckInRecord::getActualTime, firstDay, lastDay);

        // 成功天数
        long successDays = count(queryWrapper);
        // 成功进度 取整数部分
        long successRate = successDays * 100 / count;

        DashboardData dashboardData = new DashboardData();
        dashboardData.setMonthlyStats(new DashboardData.MonthlyStats(successDays, successRate));


        // 累计早睡天数
        queryWrapper.clear();
        queryWrapper.eq(CheckInRecord::getUserId, StpUserUtil.getLoginIdAsLong())
                .eq(CheckInRecord::getStatus, "success");

        long totalSuccessDays = count(queryWrapper);
        // 可用积分 取用户表
        LambdaQueryWrapper<User> userQueryWrapper = new LambdaQueryWrapper<>();
        userQueryWrapper.select(User::getPoints);
        userQueryWrapper.eq(User::getId, StpUserUtil.getLoginIdAsLong());

        User user = userDao.selectOne(userQueryWrapper);
        dashboardData.setTotalStats(new DashboardData.TotalStats(totalSuccessDays, user.getPoints()));

        List<CheckInRecordVo> checkInRecordData =
                baseMapper.listCheckInRecordData(StpUserUtil.getLoginIdAsLong());


        dashboardData.setCheckInRecords(checkInRecordData);
        return dashboardData;
    }

    @Override
    public PageQueryInfo<List<CheckInRecordQueryAllVo>> getAll(PageQueryParamVo pageQueryParamVo) {
        IPage<CheckInRecordQueryAllVo> page =
                this.baseMapper.getAll(new Page<>(pageQueryParamVo.getPageNum(),
                        pageQueryParamVo.getPageSize()));
        return PageQueryInfo.of(page.getRecords(), page.getTotal());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(CheckInRecordAddVo checkInRecordAddVo) {
        // 1. 查询用户
        User user = userDao.selectById(checkInRecordAddVo.getUserId());
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        LocalDateTime actualTime = checkInRecordAddVo.getActualTime();
        String targetSleepTime = user.getTargetSleepTime();

        if (targetSleepTime == null || !targetSleepTime.matches("\\d{2}:\\d{2}:\\d{2}")) {
            throw new RuntimeException("目标睡眠时间格式错误");
        }

        // 解析目标时间
        LocalTime targetLocalTime = LocalTime.parse(targetSleepTime);
        LocalDate actualDate = actualTime.toLocalDate();

        // 构造两个候选目标时间
        LocalDateTime todayTargetTime = LocalDateTime.of(actualDate, targetLocalTime);
        LocalDateTime tomorrowTargetTime = todayTargetTime.plusDays(1);

        // 计算与两个目标时间的距离（毫秒）
        long diffToday = Math.abs(Duration.between(actualTime, todayTargetTime).toMillis());
        long diffTomorrow = Math.abs(Duration.between(actualTime, tomorrowTargetTime).toMillis());

        // 选择更接近的时间作为 effectiveTargetTime
        LocalDateTime effectiveTargetTime = diffToday <= diffTomorrow ? todayTargetTime : tomorrowTargetTime;

        // 2. 计算积分
        Integer pointsEarned = 0;
        String status = checkInRecordAddVo.getStatus();
        if ("success".equalsIgnoreCase(status)) {
            pointsEarned = 10;
        }

        // 3. 添加签到记录
        CheckInRecord record = new CheckInRecord();
        record.setUserId(user.getId());
        record.setTargetTime(effectiveTargetTime);
        record.setActualTime(actualTime);
        record.setPointsEarned(pointsEarned);
        record.setStatus(status);

        if (this.baseMapper.insert(record) == 0) {
            throw new RuntimeException("添加签到记录失败");
        }

        // 4. 更新用户总积分
        user.setPoints(user.getPoints() + pointsEarned);
        if (userDao.updateById(user) == 0) {
            throw new RuntimeException("更新用户积分失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(Long id) {
        // 查询这个记录
        CheckInRecord record = this.baseMapper.selectById(id);
        // 查询用户
        User user = userDao.selectById(record.getUserId());
        // 扣除用户积分
        user.setPoints(user.getPoints() - record.getPointsEarned());

        if (userDao.updateById(user) == 0) {
            throw new RuntimeException("更新用户积分失败");
        }

        // 删除
        if (this.baseMapper.deleteById(id) == 0) {
            throw new RuntimeException("删除签到记录失败");
        }
        return true;
    }
}
